<?php
/**
 * crcl/base
 *
 * Copyright 2013 CRCL - André Sabosch - info@crcl.de
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace crcl\base\structure;

interface IItem
{
  public static function createFromConfig($sUri);

  public function setItemName($sName);
  public function getItemName();

  public function setDsn($sDsn);
  public function getDsn();

  public function setField($sField, field\IConcrete $oField);
  public function hasField($sField);

  public function __get($sField);

  public function commit();
  public function rollback();
  public function isDirty();

  public function toArray();
  public function toArrayDirty();

  public function persistance();
}

class Item implements IItem, \Iterator
{
  const CONFIG_META = 'meta';
  const CONFIG_META_FIELDS = 'fields';
  const CONFIG_META_NAME = 'name';
  const CONFIG_META_DSN = 'dsn';

  const OPERATION_SET = 'set';
  const OPERATION_GET = 'get';
  const OPERATION_FIELD = 'field';
  const OPERATION_HAS = 'has';

  public static function createFromConfig($sUri)
  {
    $oConfig = \crcl\base\Config::reader($sUri);
    $oItem = new self;

    $sName = $oConfig->setDefault(\NULL)->get(self::CONFIG_META, self::CONFIG_META_NAME);
    if (\is_null($sName))
    {
      throw new Exception('Missing name.', Exception::ERROR_ITEM_MISSINGNAME,
        array('meta' => $oConfig->setDefault(array())->get(self::CONFIG_META)));
    }
    $oItem->setItemName($sName);

    $sDsn = $oConfig->setDefault(\NULL)->get(self::CONFIG_META, self::CONFIG_META_DSN);
    $oItem->setDsn($sDsn);

    $aFields = $oConfig->setDefault(array())->get(self::CONFIG_META, self::CONFIG_META_FIELDS);
    if (empty($aFields))
    {
      throw new Exception('Missing fields.', Exception::ERROR_ITEM_MISSINGFIELDS,
        array('meta' => $oConfig->setDefault(array())->get(self::CONFIG_META)));
    }
    foreach ($aFields as $sField)
    {
      $aField = $oConfig->setDefault(array())->get($sField);
      if (empty($aField))
      {
        throw new Exception('Missing field.', Exception::ERROR_ITEM_MISSINGFIELD);
      }

      $oField = Field::getInstanceFromConfig($aField);
      $oItem->setField($sField, $oField);
    }

    return $oItem;
  }

  public function setItemName($sName)
  {
    $this->sName = $sName;
  }

  public function getItemName()
  {
    return $this->sName;
  }

  public function setDsn($sDsn)
  {
    $this->sDsn = $sDsn;
  }

  public function getDsn()
  {
    return $this->sDsn;
  }

  public function setField($sField, field\IConcrete $oField)
  {
    $this->aFields[$sField] = $oField;
  }

  public function hasField($sField)
  {
    if (isset($this->aFields[$sField]))
    {
      return \TRUE;
    }
    return \FALSE;
  }

  public function __get($sField)
  {
    if (\array_key_exists($sField, $this->aFields))
    {
      return $this->aFields[$sField];
    }
    if ($sField == 'id')
    {
      $this->persistance();
      return $this->aFields[$sField];
    }

    throw new Exception('Unknown field.',
      \crcl\base\structure\Exception::ERROR_ITEM_UNKNOWNFIELD,
      array('field' => $sField));
  }

  public function commit()
  {
    foreach ($this->aFields as $oField)
    {
      $oField->commit();
    }
  }

  public function rollback()
  {
    foreach ($this->aFields as $oField)
    {
      $oField->rollback();
    }
  }

  public function isDirty()
  {
    foreach ($this->aFields as $oField)
    {
      if ($oField->isDirty())
      {
        return \TRUE;
      }
    }
    return \FALSE;
  }

  public function toArray()
  {
    $aReturn = array();
    foreach ($this->aFields as $sKey => $oField)
    {
      $aReturn[$sKey] = $oField->get();
    }
    return $aReturn;
  }

  public function toArrayDirty()
  {
    $aReturn = array();
    foreach ($this->aFields as $sKey => $oField)
    {
      if ($oField->isDirty())
      {
        $aReturn[$sKey] = $oField->get();
      }
    }
    return $aReturn;
  }

  public function fromArray($aData)
  {
    foreach ($aData as $sKey => $value)
    {
      $this->$sKey->set($value);
    }
  }

  public function getFieldnames()
  {
    return \array_keys($this->aFields);
  }

  public function rewind()
  {
    return reset($this->aFields);
  }

  public function current()
  {
    return current($this->aFields);
  }

  public function key()
  {
    return key($this->aFields);;
  }

  public function next()
  {
    return next($this->aFields);
  }

  public function valid()
  {
    return key($this->aFields) !== \NULL;
  }

  public function persistance()
  {
    return \crcl\base\persistance\Item::create($this);
  }

  /** PRIVATE *************************************************************************************/

  private $sName = \NULL;
  private $sDsn = \NULL;
  private $aFields = array();
}